import re
from logging import getLogger

from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core import mail
from django.urls import reverse
from django_redis import get_redis_connection
from itsdangerous import TimedJSONWebSignatureSerializer as TJSerialzier, \
    BadData

from meiduo.utils.mei_duo import TimeOp
from users.models import User
from .constants import *

logger = getLogger('django')


def get_user(username):
    """
    由手机号或用户名获取用户对象
    :param username:
    :return:用户对象、None
    """
    try:
        if re.match(fusername, username):
            user = User.objects.get(mobile=username)
        elif re.match(fmobile, username):
            user = User.objects.get(username=username)
        else:
            user = None
    except User.DoesNotExist:
        user = None

    return user


class CustomAuthBackend(ModelBackend):
    """
    自定义用户认证后端
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        """
        重写认证方法，实现多账号登录
        :param request: 请求对象
        :param username: 用户名
        :param password: 密码
        :param kwargs: 其他参数
        :return: user 、None
        """
        try:
            # 根据传入的username获取user对象。username可以是手机号也可以是账号
            user = get_user(username)
            # 校验user是否存在并校验密码是否正确
            if user and user.check_password(password):
                return user
            else:
                return None
        except Exception as e:
            logger.error(e)
            return None


def check(username=None, mobile=None, password=None, email=None, tel=None):
    """
    校验用户名、手机号、密码格式
    :param username:
    :param mobile:
    :param password:
    :return: 错误字典 key:错误项，value:错误信息，为空表示都没错
    """
    error = {}

    if username:
        if not re.match(fusername, username):
            error['username'] = '用户名必须是由5-20个字母、数字、下划线或横杠组成'

    if mobile:
        if not re.match(fmobile, mobile):
            error['mobile'] = '手机号必须是11位的有效号码'

    if tel:
        if not re.match(ftel, tel):
            error['tel'] = 'tel格式有误'

    if password:
        if not re.match(fpswd, password):
            error['password'] = '密码必须是由8-20个字母、数字组成'

    if email:
        if not re.match(femail, email):
            error['email'] = '邮箱格式不正确'

    return error


def query_db(model, **kwargs):
    """
    查询数据库
    :param model: 模型类 <=> 表格
    :param kwargs: 属性=值 <=> 字段=值
    :return: 字典{‘count’:总数，‘models’:模型对象列表}
    """
    query = model.objects.filter(**kwargs)

    return {'count': query.count(), 'models': query}


class UserLock:
    """
    账号锁定工具
    """

    def __init__(self, db_name):
        self.redis_conn = get_redis_connection(db_name)
        self.count = 0
        self.lock = False
        self.frozen = False
        self.info = '正常'
        self.mobile = ''
        self.time = 0

    def get_lock_info(self, mobile=None):
        """获取状态信息"""
        if mobile:
            self.get_user_state(mobile)
        if self.count >= SECRET_ERROR_CHANCE:
            # 冻结
            time = self.redis_conn.ttl('lock_%s' % self.mobile)
            self.frozen = True
            self.time = time
            self.info = frozen_tip % TimeOp.s2hms(time)
        else:
            self.info = lock_tip % (SECRET_ERROR_CHANCE - self.count)

    def get_user_state(self, mobile):
        """
        获取账号状态：冻结、锁定/正常
        """
        self.mobile = mobile
        count = self.redis_conn.get('lock_%s' % mobile)
        # 获取账号状态
        if count:  # 锁定
            self.lock = True
            self.count = int(count.decode())
        self.get_lock_info()

    def set_count(self, mobile=None, count=None):
        """
        锁定账户，设置锁定次数
        """
        if mobile and count:
            self.redis_conn.set('lock_%s' % mobile, count, LOGIN_LOCK_TIME)
        elif self.mobile:
            self.redis_conn.set('lock_%s' % self.mobile, self.count,
                                LOGIN_LOCK_TIME)
        else:
            ValueError('需要两个参数, 或先调用get_user_state方法')

    def unlock(self, mobile=None):
        """解除账号锁定/冻结"""
        if mobile:
            self.redis_conn.delete('lock_%s' % mobile)
        elif self.mobile:
            self.redis_conn.delete('lock_%s' % self.mobile)
        else:
            ValueError('缺少一个参数, 或先调用get_user_state方法')


def generate_email_url(user):
    """
    生成邮箱链接
    :param user: 用户对象
    """
    # 创建序列化器对象
    # s = TimedJSONWebSignatureSerializer('秘钥:越复杂越安全', '过期时间')
    s = TJSerialzier(settings.SECRET_KEY, EMAIL_URL_EXPIRES)
    # 准备待加密的字典数据
    data = {'user_id': user.id, 'email': user.email}
    # 调用dumps方法进行加密:类型是bytes
    token = s.dumps(data).decode()
    # 合成url并返回
    return settings.WEB_HOSTS + reverse(
        'users:active_email') + '?token=' + token


def check_email_token(token):
    """
    解密 email_token
    :param token: 激活token
    :return: 用户对象
    """
    # 序列化器需与加密时使用的相同
    s = TJSerialzier(settings.SECRET_KEY, EMAIL_URL_EXPIRES)
    # 解开openid密文
    try:
        data = s.loads(token)
    except BadData:
        # token过期
        return
    else:
        try:
            user = User.objects.filter(id=data['user_id'], email=data['email'])
        except Exception as e:
            logger.error(e)
            return
        return user


def send_active_email(email, url):
    """
    发送邮箱激活邮件
    :return:
    """
    subject = '美多用户邮箱激活'
    message = ''
    html = '<a href="%s">点击激活</a>' % url
    # [email] 收件人列表
    try:
        res = mail.send_mail(subject, message, settings.EMAIL_FROM, [email],
                             html_message=html)
    except Exception as e:
        user = User.objects.filter(email=email)
        logger.error('[发送激活邮件]收件人:[%s] 用户:%s [异常]' % (email, list(user)))
        raise
    # 1 发送成功， 0 发送失败
    if res:
        logger.info('[发送激活邮件]收件人:[%s] [发送成功]' % email)
    else:
        logger.info('[发送激活邮件]收件人:[%s] [发送失败]' % email)
